/*
 * AFrame (原 m_data) 的 module 管理模块
 * @author Zdream
 * @since 0.2.0
 */

/*
 * 模块、原型的部分
 */
m_data.m = {
	/*
	 * 所有模块的集合, 用名称 - 模块键值对.
	 * String - Object
	 * 
	 * 其中, 名称就是场景的模块. scenes 项的元素里面的 module 就对应这个值.
	 */
	modules: {},

	/*
	 * 要播放的模块总数. 不等于模块总数.
	 * Number(Integer)
	 */
	moduleCount: 0,

	/*
	 * 当前模块的索引. 指向 modulesOrder 中的元素
	 * Number(Integer)
	 * 
	 * 等于 moduleCount 就意味着播放完成
	 */
	curModuleIdx: -1,
	
	/*
	 * 在播放过程中, 第几个播放使用的模块需要补充怎样的数据
	 * Array(Object)
	 * 
	 * 每个需要播放的模块的配置都是不同的,
	 * 即使是一个工程内相同的模块, 在前面播放的和在后面播放的, 配置数据都会有所差异.
	 * 
	 * 该模块调度器 m_data 将在调用该模块前自动设置数据.
	 * @since 0.1.1
	 */
	sequenceConfig: [],
	
	/*
	 * 在播放过程中, 名称叫什么的模块需要补充怎样的数据
	 * Object(String - Object)
	 * 
	 * @since 0.1.1
	 */
	nameConfig: {},
	
	/*
	 * 模块的执行推荐顺序. 里面存放着 module 的名称的列表.
	 * 如果这个参数为 null, 则以 addModules 的加载顺序逐个执行 module (待测试).
	 * Array(Object)
	 * 
	 * 已知的是 m_monitor 在初始化阶段会利用这个数组来控制播放的顺序.
	 * 
	 * 数组的元素为:
	 * {
	 * 	"name": ..., // String, 模块名称. 用于指示某个特定模块
	 * 	"id": ..., // String, 调用模块标识. 大部分情况等同于 name.
	 * 				如果一个项目中需要加载多次同一个模块, 用这个参数进行区分.
	 * }
	 */
	modulesOrder: [],

	/*
	 * 播放阶段
	 * Number(Integer), 类似于枚举:
	 * 
	 * 1. 播放还未开始
	 * 2. 正在播放
	 * 3. 播放结束
	 */
	stage: 1,

	/**
	 * 检查 modulesOrder.
	 * 如果 modulesOrder 为空, 将以随机顺序建立 modulesOrder.
	 */
	scanModulesOrder: function() {
		if (this.modulesOrder.length > 0) {
			return;
		}

		for (var m in this.modules) {
			this.modulesOrder.push({
				name: this.modules[m].name,
				id: this.modules[m].name,
			});
			this.moduleCount++;
		}
	},

	/**
	 * 切换到下一个模块所需要进行的过渡操作,
	 * 配置下一个模块所需要的环境.
	 * 
	 * @return
	 *   Boolean: 是否有下一个模块.
	 */
	switchModule: function() {
		if (this.stage == 2) {
			m_data.s.reset();
			this.curModuleIdx ++;
		} else {
			return false;
		}

		m_data.s.reset();
		if (this.curModuleIdx == this.moduleCount) {
			// 所有模块播放完成
			this.stage = 3;
			m_data.curModule = null;
			return false;
		}
		var head = this.modulesOrder[this.curModuleIdx];
		
		this.initModule(head.name);
		m_data.curModule = this.modules[head.name];
		
		// 版本 0.1.1 中加入的流程 自动设置参数
		this.injectConfig();
		
		m_data.e.release("next-module", {
			index: this.curModuleIdx,
			name: this.modulesOrder[this.curModuleIdx].name,
			id: this.modulesOrder[this.curModuleIdx].id
		});
		return true;
	},

	/**
	 * 初始化 Module
	 */
	initModule: function(mname) {
		var module = this.modules[mname];
		if (module == null) {
			console.log(mname + " 没有对应的 module");
			return;
		}
		
		var initFunc = module.init;
		if (typeof initFunc == 'function') {
			initFunc.call(module);
		} else {
			// TODO 其它解决方法
		}
	},

	/**
	 * 为将要播放的 Module 注入 config 数据.
	 * 
	 * (越后面的 config 的优先度越大, 因为后面的 config 可以覆盖前面的)
	 * 注入顺序为:
	 * 1. 名称对应的 config 数据
	 * 2. index 对应的 config 数据
	 * 
	 * @since v0.1.1
	 */
	injectConfig: function() {
		// nameConfig
		var mname = this.modulesOrder[this.curModuleIdx].name;
		if (this.nameConfig[mname] != null) {
			var conf = this.nameConfig[mname];
			for (var key in conf) {
				m_data.judgeConfig(m_data.curModule.name, key, conf[key]);
			}
		}
		
		// sequenceConfig
		if (this.sequenceConfig[this.curModuleIdx] != null) {
			var conf = this.sequenceConfig[this.curModuleIdx];
			for (var key in conf) {
				m_data.judgeConfig(m_data.curModule.name, key, conf[key]);
			}
		}
	},

	/**
	 * 添加 Module 的顺序
	 */
	addModuleOrder: function(name, id) {
		this.modulesOrder.push({
			name: name,
			id: id,
		});
		this.moduleCount++;
	},
	
	/**
	 * 逐个 Module 做一个操作
	 * @param handler
	 *   function(module)
	 */
	foreach: function(handler) {
		for (var m in this.modules) {
			handler(this.modules[m]);
		}
	}
}
